#include "usart0.h"
#include <string.h>



#if USE_USART_0
//串口接收缓冲区大小 
static uint8_t g_recv_buff [USART0_RX_BUFFER_LEN];   // 接收缓冲区
static uint32_t rx_cnt = 0;

#if USE_USART0_DMA
/**
 *@desc  : dma 结合 usart0 发送配置  从内存到外设
 * @date  : 2024-11-12
 * @author: AnRui
 */
void DMA_tx_config() {

    uint32_t dmax = USART0_TX_DMA; 
    uint32_t dmax_rcu = USART0_TX_DMA_RCU;
    uint32_t dmax_ch = USART0_TX_DMA_CHANNEL;
    uint32_t damx_sub = USART0_TX_DMA_SUB;
    uint32_t dmax_dirction = DMA_MEMORY_TO_PERIPH;
    uint32_t dmax_src_inc = DMA_MEMORY_INCREASE_ENABLE;
    uint32_t dmax_src_width = DMA_MEMORY_WIDTH_8BIT;
    uint32_t dmax_dst = (uint32_t)(&USART_DATA(USART0));// usart0 寄存器地址
    uint32_t dmax_dst_inc = DMA_PERIPH_INCREASE_DISABLE;


    /***************** DMA 内存到外设 *******************/
    // 时钟
    rcu_periph_clock_enable(dmax_rcu);
    // 重置dma
    dma_deinit(dmax, dmax_ch);

    // dma 配置
    dma_single_data_parameter_struct dsdps;
    dma_single_data_para_struct_init(&dsdps);

    // 方向
    dsdps.direction = dmax_dirction;
    // 内存: src 
    // dsdps.memory0_addr = (uint32_t)src;
    dsdps.memory_inc = dmax_src_inc;

    // 外设: dst  (&USART_DATA(USART0)) 这个是usart数据寄存器地址 
    dsdps.periph_addr = dmax_dst;
    dsdps.periph_inc = dmax_dst_inc;

    // 数据长度 数据长度不确定 
    // dsdps.number = ARR_LEN;
    // 数据宽度
    dsdps.periph_memory_width = dmax_src_width;
    // 传输优先级
    dsdps.priority = USART0_TX_DMA_PRIORITY;
    dma_single_data_mode_init(dmax, dmax_ch, &dsdps);

    // 配置 dma 子连接
    dma_channel_subperipheral_select(dmax, dmax_ch, damx_sub);

}

/**
 *@desc  : dma 结合 usart0 接收配置  从外设到内存
 * @date  : 2024-11-12
 * @author: AnRui
 */
void DMA_rx_config() {

    uint32_t dmax = USART0_RX_DMA;
    uint32_t dmax_rcu = USART0_RX_DMA_RCU;
    uint32_t dmax_ch = USART0_RX_DMA_CHANNEL;
    uint32_t damx_sub = USART0_RX_DMA_SUB;
    uint32_t dma_priority = USART0_RX_DMA_PRIORITY;
    uint32_t dmax_dirction = DMA_PERIPH_TO_MEMORY;
    // 外设
    uint32_t dmax_src = (uint32_t)(&USART_DATA(USART0));
    uint32_t dmax_src_inc = DMA_PERIPH_INCREASE_DISABLE; // 外设地址 不能增长
    uint32_t dmax_src_width = DMA_PERIPH_WIDTH_8BIT;//注意不要写成DMA_MEMORY_WIDTH_8BIT
    uint32_t dma_src_len = USART0_RX_BUFFER_LEN;// 寄存器只有一个byte，但是接收的数据量是不确定的。长度可以是动态的
    // 目标内存
    uint32_t dmax_dst = (uint32_t)g_recv_buff;
    uint32_t dmax_dst_inc = DMA_MEMORY_INCREASE_ENABLE;
    /***************** DMA 内存到外设 *******************/
    // 时钟
    rcu_periph_clock_enable(dmax_rcu);
    // 重置dma
    dma_deinit(dmax, dmax_ch);

    //////// dma 配置
    dma_single_data_parameter_struct dsdps;
    dma_single_data_para_struct_init(&dsdps);

    // 方向
    dsdps.direction = dmax_dirction;

    // 源头（外设）
    dsdps.periph_addr = dmax_src;
    dsdps.periph_inc = dmax_src_inc;
    dsdps.periph_memory_width = dmax_src_width;// 每个数据的宽度1byte 8bit
    dsdps.number = dma_src_len; // 在接收的时候 不知道 要传输多少个数据过来 在设置一个 之后在结束的时候 直接终止 重新开 
    // 目标（内存）
    dsdps.memory0_addr = dmax_dst;
    dsdps.memory_inc = dmax_dst_inc;

    // 传输优先级
    dsdps.priority = dma_priority;
    dma_single_data_mode_init(dmax, dmax_ch, &dsdps);

    // 配置 dma 子连接
    dma_channel_subperipheral_select(dmax, dmax_ch, damx_sub);

    // 通知DMA去干活  todo 告诉dam 监控串口寄存器  有数据 自动执行 放到缓冲区 
    dma_channel_enable(dmax, dmax_ch);
}
#endif

/**
 *  @desc  :usart0 初始化
 * @date  : 2025-02-12
 * @author: AnRui
 */

void usart0_init() {
    //发送引脚配置
    uint32_t usartx_tx_rcu = USART0_TX_RCU;
    uint32_t usartx_tx_port = USART0_TX_PORT;
    uint32_t usartx_tx_pin = USART0_TX_PIN;
    uint32_t usartx_tx_af = USART0_TX_AF;
    // 接收引脚配置
    uint32_t usartx_rx_rcu = USART0_RX_RCU;
    uint32_t usartx_rx_port = USART0_RX_PORT;
    uint32_t usartx_rx_pin = USART0_RX_PIN;
    uint32_t usartx_rx_af = USART0_RX_AF;
    // usart相关
    uint32_t usartx = USART0;
    uint32_t usartx_rcu = RCU_USART0;
    uint32_t usartx_irqn = USART0_IRQn;
    // usart 配置
    uint32_t usartx_p_baudrate = USART0_BAUDRATE;
    uint32_t usartx_p_parity = USART_PM_NONE; // 校验位
    uint32_t usartx_p_wl = USART_WL_8BIT; //数据宽度 
    uint32_t usartx_p_stop_bit = USART_STB_1BIT; // 1位停止位
    uint32_t usartx_p_data_first = USART_MSBF_LSB; // 先发送低位
    /************** gpio config **************/
    // tx
    rcu_periph_clock_enable(usartx_tx_rcu);  // 配置时钟
    gpio_mode_set(usartx_tx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_tx_pin);
    gpio_af_set(usartx_tx_port, usartx_tx_af, usartx_tx_pin);
    gpio_output_options_set(usartx_tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_tx_pin);
    // rx
    rcu_periph_clock_enable(usartx_rx_rcu); // 配置时钟
    gpio_mode_set(usartx_rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, usartx_rx_pin);
    gpio_af_set(usartx_rx_port, usartx_rx_af, usartx_rx_pin);
    //gpio_output_options_set(usartx_rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, usartx_rx_pin);

    /************** usart config **************/
    // 串口时钟
    rcu_periph_clock_enable(RCU_USART0);
    // USART复位
    usart_deinit(usartx);

    usart_baudrate_set(usartx, usartx_p_baudrate);  // 波特率
    usart_parity_config(usartx, usartx_p_parity); // 校验位
    usart_word_length_set(usartx, usartx_p_wl); // 数据位数
    usart_stop_bit_set(usartx, usartx_p_stop_bit); // 停止位
    usart_data_first_config(usartx, usartx_p_data_first); // 先发送高位还是低位

    // 发送功能配置 
    usart_transmit_config(usartx, USART_TRANSMIT_ENABLE);
    // 接收功能配置 
    usart_receive_config(usartx, USART_RECEIVE_ENABLE);

    // 接收中断配置
    nvic_irq_enable(usartx_irqn, USART0_NVIC);
    // usart int rbne
    usart_interrupt_enable(usartx, USART_INT_RBNE);  // 接收到数据
    usart_interrupt_enable(usartx, USART_INT_IDLE);  // 空闲中断

#if USE_USART0_DMA
    // DMA配置------------------------------------------------
    // 开启DMA发送
    usart_dma_transmit_config(usartx, USART_TRANSMIT_DMA_ENABLE);
    // 开启DMA接收
    usart_dma_receive_config(usartx, USART_RECEIVE_DMA_ENABLE);
    DMA_tx_config();
    DMA_rx_config();
#endif
    // 使能串口
    usart_enable(usartx);

}



void usart0_send_byte(uint8_t data) {
#if USE_USART0_DMA
    usart0_send_data(&data, 1);
#else
    //通过USART发送
    usart_data_transmit(USART0, data);
    //判断缓冲区是否已经空了
    while (RESET == usart_flag_get(USART0, USART_FLAG_TBE));
#endif
}


void usart0_send_data(uint8_t* data, uint32_t len) {
#if USE_USART0_DMA
    dma_memory_address_config(DMA1, DMA_CH7, DMA_MEMORY_0, (uint32_t)(data));
    dma_transfer_number_config(DMA1, DMA_CH7, len);

    // 触发传输
    dma_channel_enable(DMA1, DMA_CH7);

    // 等待DMA传输完成
    while (RESET == dma_flag_get(DMA1, DMA_CH7, DMA_FLAG_FTF));
    // 清理标记 如果不清理 下一次发送数据的时候 回到配置的首地址 
    dma_flag_clear(DMA1, DMA_CH7, DMA_FLAG_FTF);

#else
    while (data && *data && len) {
        usart0_send_byte((uint8_t)(*data));
        data++;
        len--;
    }

#endif 
}

void usart0_send_string(uint8_t* data) {
    //满足: 1.data指针不为空  2.发送的数据不是\0结束标记
    while (data && *data) {
        usart0_send_byte((uint8_t)(*data));
        data++;
    }
}

#if USE_USART0_PRINTF 
//重写fputc方法  调用printf,会自动调用这个方法实现打印
int fputc(int ch, FILE* f) {
    usart0_send_byte((uint8_t)(ch));
    return ch;
}
#endif


void USART0_IRQHandler(void) {
#if USE_USART0_DMA
    if (SET == usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) {
        //闲置 清除标志的作用 
        usart_data_receive(USART0);
        //usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);

        //停止dma搬运
        dma_channel_disable(DMA1, DMA_CH2);
        dma_flag_clear(DMA1, DMA_CH2, DMA_FLAG_FTF);// 重置计数器

        // rx_cnt：接收数据的长度，谁知道，dma搬运的数量
        // remaining: 剩下的
        rx_cnt = USART0_RX_BUFFER_LEN - dma_transfer_number_get(DMA1, DMA_CH2);
        g_recv_buff [rx_cnt] = '\0';
#if USE_USART0_RECIVE
        usart0_on_recv(g_recv_buff, rx_cnt);
#endif
        //开启dma搬运
        dma_channel_enable(DMA1, DMA_CH2);

        //清理缓冲区
        rx_cnt = 0;
    }
#else
    // 检测 usart 中断标志
    if ((usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) == SET) {
        // 接收数据
        uint16_t value = usart_data_receive(USART0);
        // 放到缓冲区中
        g_recv_buff [rx_cnt] = value;
        // 缓冲区数据长度加1
        rx_cnt++;
        // 这步可以省略 接收数据的过程汇总就已经清楚 触发的标志了
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
    }
    if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) == SET) {
        //读取缓冲区,清空缓冲区
        //没有专门清楚标志的函数 所以使用接收函数 
        usart_data_receive(USART0);
        //缓冲区添加结束符
        g_recv_buff [rx_cnt] = '\0';
        //  g_recv_buff为接收的数据，g_recv_length为接收的长度

#if USE_USART0_RECIVE
        usart0_on_recv(g_recv_buff, rx_cnt);
#endif
        rx_cnt = 0;
    }
#endif

}





#endif